package cn.wgx.commons.config.web;

import cn.wgx.commons.security.shiro.cache.JedisCacheManager;
import cn.wgx.commons.security.shiro.filter.FormAuthenticationFilter;
import cn.wgx.commons.security.shiro.filter.KickoutSessionControlFilter;
import cn.wgx.commons.security.shiro.realm.CustomRealm;
import cn.wgx.commons.security.shiro.session.JedisSessionDAO;
import cn.wgx.commons.security.shiro.session.SessionManager;
import cn.wgx.commons.util.IdGen;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;


@Configuration
public class ShiroConfig {

    //sessionID名称,__wgx_sid
    private static String SESSION_ID_NAME = "__wgx_sid";
    //cookies过期时间,秒
    private static Integer COOKIES_MAX_AGE = 1800000000;
    //session有效期,毫秒
    private static Long SESSION_TIMEOUT = 2592000000l;

    @Bean
    public cn.wgx.commons.security.shiro.session.SessionManager sessionManager(SessionDAO sessionDAO, CacheManager shiroCacheManager, Cookie sessionIdCookie) {
        cn.wgx.commons.security.shiro.session.SessionManager sessionManager = new cn.wgx.commons.security.shiro.session.SessionManager();
        sessionManager.setGlobalSessionTimeout(SESSION_TIMEOUT);
        sessionManager.setSessionValidationInterval(86400000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionDAO(sessionDAO);
        sessionManager.setCacheManager(shiroCacheManager);
        sessionManager.setSessionIdCookie(sessionIdCookie);
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }
    /**
     * Shiro的过滤器链
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager,
                                              CacheManager shiroCacheManager,
                                              cn.wgx.commons.security.shiro.session.SessionDAO sessionDAO) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/api/login");//默认的登陆访问url
        shiroFilter.setSuccessUrl("/api/login/success");//登陆成功后跳转的url
        shiroFilter.setUnauthorizedUrl("error/403.html");//没有权限跳转的url

        HashMap<String, Filter> myFilters = new HashMap<>();

        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
        formAuthenticationFilter.setRememberMeParam("rememberMe");

        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
        kickoutSessionControlFilter.setMaxSession(3);//同一账号最多几人登录
        kickoutSessionControlFilter.setCacheManager(shiroCacheManager);
        kickoutSessionControlFilter.setSessionDAO(sessionDAO);
        myFilters.put("auth", formAuthenticationFilter);
        myFilters.put("kick", kickoutSessionControlFilter);

        shiroFilter.setFilters(myFilters);//过滤器

        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("/resources/**", "anon");
        linkedHashMap.put("/api/login", "auth");
        linkedHashMap.put("/api/**", "kick");
        shiroFilter.setFilterChainDefinitionMap(linkedHashMap);//过滤链
        return shiroFilter;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(CacheManager shiroCacheManager,
                                                                SessionManager sessionManager,
                                                                CookieRememberMeManager cookieRememberMeManager) {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(customRealm());
        defaultSecurityManager.setCacheManager(shiroCacheManager);
        defaultSecurityManager.setSessionManager(sessionManager);
        defaultSecurityManager.setRememberMeManager(cookieRememberMeManager);
        return defaultSecurityManager;
    }

    @Bean
    public JedisCacheManager shiroCacheManager() {
        JedisCacheManager jedisCacheManager = new JedisCacheManager();
        jedisCacheManager.setCacheKeyPrefix("shiro_cache_");
        return jedisCacheManager;
    }

    @Bean
    public JedisSessionDAO sessionDAO() {
        JedisSessionDAO jedisSessionDAO = new JedisSessionDAO();
        jedisSessionDAO.setSessionKeyPrefix("shiro-persistans-SessionCache-");
        jedisSessionDAO.setSessionIdGenerator(new IdGen());
        return jedisSessionDAO;
    }


    //@Bean
    public ExecutorServiceSessionValidationScheduler quartzSessionValidationScheduler(SessionManager sessionManager) {
        ExecutorServiceSessionValidationScheduler quartzSessionValidationScheduler = new ExecutorServiceSessionValidationScheduler();
        quartzSessionValidationScheduler.setInterval(1800000);
        quartzSessionValidationScheduler.setSessionManager(sessionManager);
        return quartzSessionValidationScheduler;
    }

    @Bean
    public CustomRealm customRealm() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//加密方式MD5
        hashedCredentialsMatcher.setHashIterations(1);//加密1次
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return customRealm;
    }

    //sessionID的cookies, __WGX_SID=23-9084902384akldjfkl93u
    @Bean
    public SimpleCookie sessionIdCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(SESSION_ID_NAME);
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(COOKIES_MAX_AGE);
        simpleCookie.setPath("/");
        return simpleCookie;
    }

    //记住我的Cookies, rememberMe=true
    @Bean
    public CookieRememberMeManager cookieRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        SimpleCookie simpleCookie = new SimpleCookie("rmbme");
        simpleCookie.setMaxAge(COOKIES_MAX_AGE);
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        cookieRememberMeManager.setCookie(simpleCookie);
        cookieRememberMeManager.setCipherKey(Base64.decode(Base64.encodeToString("wgxccccc88888888".getBytes())));
        return cookieRememberMeManager;
    }

    /**
     * Shiro生命周期处理器:
     * 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm)
     * 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 启用shrio授权注解拦截方式，AOP式方法级权限检查
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator app=new DefaultAdvisorAutoProxyCreator();
        app.setProxyTargetClass(true);
        return app;
    }
}
